// general_io.h

#ifndef DODO_GENERAL_IO_H
#define DODO_GENERAL_IO_H
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <string>
#include <list>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/filesystem.hpp>
#include <stdexcept>

#include <quigon/basic/basic_para.h>
#include <quigon/basic/constants.h>
#include <quigon/basic/basic_utils.h>
namespace quigon
{

// This must be instantiated with each different kind of lidar data
template<class T, class Tpara>
bool file_to_line(std::istream &file, T& line, const Tpara& para)
{
	return false;
}

template<class T, class Tpara>
bool examine_file(
        const std::string &filename,
        const Tpara& para,
        const boost::posix_time::time_period& target_period = boost::posix_time::time_period(
                                boost::posix_time::ptime(boost::posix_time::not_a_date_time),
                                boost::posix_time::seconds(0)
                        )
)
{
	return false;
}

// required fields in para
// string		"data_path"
template<class T, class Tpara>
size_t import(std::list<T>& container, const boost::posix_time::ptime& t_start, const boost::posix_time::ptime& t_end, const Tpara& para, const boost::posix_time::time_duration& max_file_duration = boost::posix_time::hours(2))
{
	try
	{
		using namespace std;
		using namespace boost::posix_time;
		using namespace boost::filesystem;

		string data_path_str = para.data_path ;
#ifdef VERBOSE
		cout << "Data Path: " << data_path_str << endl;
#endif
		if (data_path_str.length() == 0)
			data_path_str = ".";

		if (! conv_env(data_path_str))
			throw invalid_argument(string("env variable: ") + data_path_str + " not found!, please check setting files");

		path data_path(data_path_str);
		if (!exists(data_path))
			throw invalid_argument(string("path: ") + data_path_str + " not exist!");

		time_period target_period(t_start, t_end);
#ifdef VERBOSE
		cout << "Importing Period: " << target_period << endl;
#endif

		T temp;
		size_t count = 0;
		directory_iterator end_iter;
		for (directory_iterator iter(data_path); iter != end_iter; ++iter)
		{
			if (is_regular_file(iter->status()) && examine_file<T> (iter->string(), para))
			{
#ifdef VERBOSE
				cout << "File: " << iter->string() << endl;
#endif
				ifstream file(iter->string().c_str(), ios::binary);
				if (!file || file.eof())
					continue;
				//Trick or what? read and then check eof, the eof is real eof.
				while (file_to_line(file, temp, para) && !file.eof())
				{
					try
					{
#ifdef DEBUG
						cout << "DEBUG: Line Time: " << temp.t() << endl;
#endif
						if (temp.t() + max_file_duration < target_period.begin())
							break;
						if (temp.t() > target_period.last())
							break;
						if (target_period.contains(temp.t()))
						{
							container.push_back(temp);
							count++;
						}
					}
					catch (runtime_error e)
					{
#ifdef DEBUG
						cerr << "!!! runtime_error met while importing: " << e.what() << endl;
#endif
					}
				}
			}
		}
		container.sort();
		return count;
	}
	catch (...)
	{
		func_err("import", "para.data_path");
		throw;
	}
}

// TODO : automatic the attr output
class Lidar_Printer
{
public:
	Lidar_Printer(size_t num0 = 100, size_t perline0 = 10) : num(num0), perline(perline0) {}

	template<class T>
	void operator()(const T& line, std::ostream &file = std::cout) const
	{
		try
		{
			using namespace std;
			using namespace boost::posix_time;

			file << "Time: " << line.t() << " \t" << line.duration() << endl;
			file << "Status: " << line.status() << endl;

			for (typename T::attr_iterator ai = line.attr_begin(); ai != line.attr_end(); ++ai)
			{
				file << ai->first << " \t" << ai->second << endl;
			}

			size_t total = num;
			if (line.size() < total)
				total = line.size();

			file << "First " << total << " data : " ;  // save a endl for the loop

			for (size_t i = 0; i < total ; ++i)
			{
				if (i % perline == 0)
					file << endl;
				file << line[i] << " \t" ;
			}
			file << endl;
		}
		catch (...)
		{
			func_err("print_line_brief");
			throw;
		}
	}
private:
	size_t num;
	size_t perline;
};

const Lidar_Printer print_line_brief;
}

#endif

